package com.econ.powercloud.ems.service.biz;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.econ.powercloud.common.base.facade.util.JacksonUtil;
import com.econ.powercloud.console.facade.enums.StatusTypeCode;
import com.econ.powercloud.ems.common.enums.DitTypeEnum;
import com.econ.powercloud.ems.common.enums.MeasureType;
import com.econ.powercloud.ems.common.enums.RemoteType;
import com.econ.powercloud.ems.constant.SysConstant;
import com.econ.powercloud.ems.dal.entity.*;
import com.econ.powercloud.ems.dal.entity.ext.IotViceProductYkYtExt;
import com.econ.powercloud.ems.pojo.*;
import com.econ.powercloud.ems.pojo.scene.FeedbackRealDataVO;
import com.econ.powercloud.ems.pojo.scene.IssueValSplitVO;
import com.econ.powercloud.ems.service.common.QueryCalDataCommonService;
import com.econ.powercloud.ems.service.common.QueryFlashDataCommonService;
import com.econ.powercloud.ems.service.dao.*;
import com.econ.powercloud.ems.util.BMSKindUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @Author : zjf
 * @Description : 设备监控service
 * @Date : 2023/6/8 17:09
 * @Modified by : zjf
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class DeviceMonitorService {

    private final EmsDeviceDAO emsDeviceDAO;
    private final IotProductYcDAO iotProductYcDAO;
    private final IotProductYxDAO iotProductYxDAO;
    private final QueryFlashDataCommonService queryFlashDataCommonService;
    private final QueryCalDataCommonService queryCalDataCommonService;
    private final ExtIotNodeDAO extIotNodeDAO;
    private final ExtIotDeviceCalculationDAO extIotDeviceCalculationDAO;
    private final ExtIotViceProductYtDAO extIotViceProductYtDAO;
    private final ExtIotViceProductYkDAO extIotViceProductYkDAO;
    private final EmsYkYtRecordDAO emsYkYtRecordDAO;
    private final EmsDeviceMonitorDataConfigDAO emsDeviceMonitorDataConfigDAO;


    /**
     * 获取设备遥测数据,展示全数据
     * @param deviceCode
     * @return
     */
    public DeviceYcDataVO getDeviceYcData(String deviceCode){
        EmsDevice emsDevice = emsDeviceDAO.selectByDeviceCode(deviceCode);
        Assert.notNull(emsDevice,"设备不存在!");
        //虚拟设备不存在遥测数据
        if(emsDevice.getProductKey() == null){
            return DeviceYcDataVO.builder().deviceCode(deviceCode).deviceName(emsDevice.getDeviceName()).dataList(Collections.emptyList()).build();
        }
        List<IotProductYc> iotProductYcs = iotProductYcDAO.selectByProductKeyAndModuleCode(emsDevice.getProductKey(),emsDevice.getModuleCode());
        EmsDeviceMonitorDataConfig monitorConfig = emsDeviceMonitorDataConfigDAO.selectOneByDeviceCode(deviceCode,DitTypeEnum.YC.getCode());
        //过滤无需展示的遥测点号
        if(monitorConfig != null){
            Pattern pattern = Pattern.compile(monitorConfig.getDitFilterMatch());
            iotProductYcs = iotProductYcs.stream().filter(d->!pattern.matcher(d.getDit()).find()).collect(Collectors.toList());
        }
        if(CollectionUtils.isEmpty(iotProductYcs)){
            return DeviceYcDataVO.builder().deviceCode(deviceCode).deviceName(emsDevice.getDeviceName()).dataList(Collections.emptyList()).build();
        }
        //需要查询的实时数据有哪些
        List<QueryDataVO> queryDataVOList = iotProductYcs.stream().map(d->
                QueryDataVO.builder().pid(emsDevice.getInitDeviceCode() + SysConstant.SPOT + d.getDit()).deviceCode(emsDevice.getDeviceCode()).initDeviceCode(emsDevice.getInitDeviceCode()).dit(d.getDit()).build())
                .collect(Collectors.toList());
        List<FlashDataVO> flashDataList = queryFlashDataCommonService.getFlashData(queryDataVOList);
        Map<String, FlashDataVO> flashDataMap = flashDataList.stream().collect(Collectors.toMap(FlashDataVO::getPid, Function.identity()));
        List<FlashVO> flashVOList = Lists.newArrayList();
        iotProductYcs.stream().forEach(d->{
            FlashDataVO flashDataVO = flashDataMap.get(emsDevice.getInitDeviceCode() + SysConstant.SPOT + d.getDit());
            if(flashDataVO != null){
                FlashVO flashVO = JacksonUtil.convertToObj(flashDataVO, FlashVO.class);
                flashVO.setValue(flashDataVO.getVal());
                flashVO.setName(flashDataVO.getRemark());
                flashVOList.add(flashVO);
            }else {
                FlashVO flashVO = FlashVO.builder()
                        .pid(emsDevice.getInitDeviceCode() + SysConstant.SPOT + d.getDit())
                        .deviceCode(deviceCode).initDeviceCode(emsDevice.getInitDeviceCode())
                        .dit(d.getDit()).ditType(DitTypeEnum.YC.getCode()).name(d.getName()).unit(d.getUnit())
                        .build();
                flashVOList.add(flashVO);
            }
        });
        return DeviceYcDataVO.builder().deviceCode(deviceCode).deviceName(emsDevice.getDeviceName()).dataList(flashVOList).build();
    }

    /**
     * 获取设备遥信数据,展示全数据
     * @param deviceCode
     * @return
     */
    public DeviceYxDataVO getDeviceYxData(String deviceCode){
        EmsDevice emsDevice = emsDeviceDAO.selectByDeviceCode(deviceCode);
        Assert.notNull(emsDevice,"设备不存在!");
        //虚拟设备不存在遥测数据
        if(emsDevice.getProductKey() == null){
            return DeviceYxDataVO.builder().deviceCode(deviceCode).deviceName(emsDevice.getDeviceName()).stateList(Collections.emptyList())
                    .faultList(Collections.emptyList()).alarmList(Collections.emptyList()).protectList(Collections.emptyList()).build();
        }
        //要查询的遥信点号量测类型有哪些
        List<Integer> statusTypeCodes = Lists.newArrayList(StatusTypeCode.ALERT.getCode(),StatusTypeCode.FAULT_LIGHT.getCode(),StatusTypeCode.EQUIPMENT_OPERATION_STATUS.getCode(),
                StatusTypeCode.PROTECT_EVENT.getCode());
        //只保留量测类型为故障,告警,运行状态,保护事件类型的遥信点号
        List<IotProductYx> iotProductYxs = iotProductYxDAO.selectByProductKeyAndModuleCode(emsDevice.getProductKey(),emsDevice.getModuleCode(), statusTypeCodes);
        if(CollectionUtils.isEmpty(iotProductYxs)){
            return DeviceYxDataVO.builder().deviceCode(deviceCode).deviceName(emsDevice.getDeviceName()).stateList(Collections.emptyList())
                    .faultList(Collections.emptyList()).alarmList(Collections.emptyList()).protectList(Collections.emptyList()).build();
        }
        //需要查询的实时数据有哪些
        List<QueryDataVO> queryDataVOList = iotProductYxs.stream().map(d->
                QueryDataVO.builder().pid(emsDevice.getInitDeviceCode() + SysConstant.SPOT + d.getDit()).deviceCode(emsDevice.getDeviceCode()).initDeviceCode(emsDevice.getInitDeviceCode()).dit(d.getDit()).build())
                .collect(Collectors.toList());
        List<FlashDataVO> flashDataList = queryFlashDataCommonService.getFlashData(queryDataVOList);
        Map<String, FlashDataVO> flashDataMap = flashDataList.stream().collect(Collectors.toMap(FlashDataVO::getPid, Function.identity()));
        //遥信实时数据按照statueCodeType分组,key:statusTypeCode
        Map<Integer,List<FlashVO>> statusTypeFlashMap = Maps.newHashMap();
        statusTypeCodes.stream().forEach(d-> statusTypeFlashMap.put(d,Lists.newArrayList()));
        iotProductYxs.stream().forEach(d->{
            FlashDataVO flashDataVO = flashDataMap.get(emsDevice.getInitDeviceCode() + SysConstant.SPOT + d.getDit());
            //区分有实时数据与没实时数据
            if(flashDataVO != null){
                FlashVO flashVO = JacksonUtil.convertToObj(flashDataVO, FlashVO.class);
                flashVO.setValue(flashDataVO.getVal());
                flashVO.setName(flashDataVO.getRemark());
                statusTypeFlashMap.get(d.getStatusTypeCode()).add(flashVO);
            }else {
                FlashVO flashVO = FlashVO.builder()
                        .pid(emsDevice.getInitDeviceCode() + SysConstant.SPOT + d.getDit())
                        .deviceCode(deviceCode).initDeviceCode(emsDevice.getInitDeviceCode())
                        .dit(d.getDit()).ditType(DitTypeEnum.YC.getCode()).name(d.getName())
                        .build();
                statusTypeFlashMap.get(d.getStatusTypeCode()).add(flashVO);
            }
        });
        return DeviceYxDataVO.builder().deviceCode(deviceCode).deviceName(emsDevice.getDeviceName())
                .stateList(statusTypeFlashMap.get(StatusTypeCode.EQUIPMENT_OPERATION_STATUS.getCode()))
                .faultList(statusTypeFlashMap.get(StatusTypeCode.FAULT_LIGHT.getCode()))
                .alarmList(statusTypeFlashMap.get(StatusTypeCode.ALERT.getCode()))
                .protectList(statusTypeFlashMap.get(StatusTypeCode.PROTECT_EVENT.getCode()))
                .build();
    }

    /**
     * 获取设备计算遥测/遥信数据
     * @param deviceCode
     * @return
     */
    public DeviceCalDataVO getDeviceCalData(String deviceCode,Integer ditType){
        Integer measureType = Objects.equals(ditType,DitTypeEnum.CAL_YX.getCode())? MeasureType.yx.getCode() : MeasureType.yc.getCode();
        EmsDevice emsDevice = emsDeviceDAO.selectByDeviceCode(deviceCode);
        Assert.notNull(emsDevice,"设备不存在!");
        ExtIotNode extIotNode = extIotNodeDAO.selectByNodeId(emsDevice.getDeviceCode());
        List<ExtIotDeviceCalculation> calculationList = extIotDeviceCalculationDAO.selectByDevicePidAndMeasureType(extIotNode.getDevicePid(), measureType);
        if(CollectionUtils.isEmpty(calculationList)){
            return DeviceCalDataVO.builder().deviceCode(deviceCode).deviceName(emsDevice.getDeviceName()).dataList(Collections.emptyList()).build();
        }
        //需要查询的实时数据有哪些
        List<QueryDataVO> queryDataVOList = calculationList.stream().map(d->
                QueryDataVO.builder().pid(d.getComputeCode()).deviceCode(emsDevice.getDeviceCode()).initDeviceCode(emsDevice.getInitDeviceCode())
                        .dit(d.getComputeCode().replace(emsDevice.getInitDeviceCode() + SysConstant.SPOT,"")).build()).collect(Collectors.toList());
        List<FlashDataVO> flashDataList = queryCalDataCommonService.getCalData(queryDataVOList);
        Map<String, FlashDataVO> flashDataMap = flashDataList.stream().collect(Collectors.toMap(FlashDataVO::getPid, Function.identity()));
        List<FlashVO> flashVOList = Lists.newArrayList();
        calculationList.stream().forEach(d->{
            FlashDataVO flashDataVO = flashDataMap.get(d.getComputeCode());
            flashVOList.add(this.getFlashDataByExtIotDeviceCalculation(emsDevice,d,flashDataVO));
        });
        return DeviceCalDataVO.builder().deviceCode(deviceCode).deviceName(emsDevice.getDeviceName()).dataList(flashVOList).build();
    }

    /**
     * 获取设备遥调点号信息
     * @param deviceCode
     * @return
     */
    public DeviceRemoteControlVO getDeviceYtData(String deviceCode){
        EmsDevice emsDevice = emsDeviceDAO.selectByDeviceCode(deviceCode);
        Assert.notNull(emsDevice,"设备不存在!");
        //虚拟设备不存在遥调数据
        if(emsDevice.getProductKey() == null){
            return DeviceRemoteControlVO.builder().deviceCode(deviceCode).deviceName(emsDevice.getDeviceName()).ykYtList(Collections.emptyList()).build();
        }
        //查询该设备下有哪些遥调(这里排除哪些把遥调当成遥控使用的遥调点号)
        List<IotViceProductYkYtExt> ytExtList = extIotViceProductYtDAO.getBaseMapper().selectYtByDeviceCodeAndModuleCode(
                emsDevice.getInitDeviceCode(),emsDevice.getModuleCode());
        if(CollectionUtils.isEmpty(ytExtList)){
            return DeviceRemoteControlVO.builder().deviceCode(deviceCode).deviceName(emsDevice.getDeviceName()).ykYtList(Collections.emptyList()).build();
        }
        List<DeviceYkYtVO> deviceYkYtVOList = Lists.newArrayList();
        List<QueryDataVO> queryDataVOList = Lists.newArrayList();
        ytExtList.stream().forEach(d->{
            DeviceYkYtVO deviceYkYtVO = DeviceYkYtVO.builder()
                    .ditName(d.getRemark()).remoteType(d.getRemoteType()).ditType(Objects.equals(d.getRemoteType(), RemoteType.YK.getCode())?DitTypeEnum.YK.getCode():DitTypeEnum.YT.getCode())
                    .dit(d.getDit()).deviceCode(deviceCode).initDeviceCode(emsDevice.getInitDeviceCode())
                    .pid(emsDevice.getInitDeviceCode() + SysConstant.SPOT + d.getDit()).unit(d.getUnit())
                    .rangeStart(d.getRangeStart()).rangeEnd(d.getRangeEnd()).sortNumber(d.getOrderNum())
                    .build();
            //获取实时点号与反馈点号
            if(d.getFeedbackRealData() != null){
                FeedbackRealDataVO dataVO = JSON.parseObject(d.getFeedbackRealData(),FeedbackRealDataVO.class);
                if(dataVO.getFeedbackDit() != null){
                    //封装需要查询的实时值
                    String actualPid = emsDevice.getInitDeviceCode() + SysConstant.SPOT + dataVO.getRealDit();
                    queryDataVOList.add(QueryDataVO.builder()
                            .pid(actualPid).deviceCode(deviceCode).dit(dataVO.getRealDit()).initDeviceCode(emsDevice.getInitDeviceCode())
                            .build());
                    deviceYkYtVO.setActualPid(actualPid);
                }
                if(dataVO.getRealDit() != null){
                    //封装需要查询的反馈值
                    String feedBackPid = emsDevice.getInitDeviceCode() + SysConstant.SPOT + dataVO.getFeedbackDit();
                    queryDataVOList.add(QueryDataVO.builder()
                            .pid(feedBackPid).deviceCode(deviceCode).dit(dataVO.getFeedbackDit()).initDeviceCode(emsDevice.getInitDeviceCode())
                            .build());
                    deviceYkYtVO.setFeedBackPid(feedBackPid);
                }
            }
            deviceYkYtVOList.add(deviceYkYtVO);
        });
        //获取点号实时数据
        List<FlashDataVO> flashDataVOList = queryFlashDataCommonService.getFlashData(queryDataVOList);
        Map<String, FlashDataVO> actualAndFeedBackMap = flashDataVOList.stream().collect(Collectors.toMap(e -> e.getInitDeviceCode() + SysConstant.SPOT + e.getDit(), Function.identity()));
        //查询遥调历史数据信息
        List<EmsYkYtRecord> ykYtRecordList = emsYkYtRecordDAO.getListByDeviceCodes(Lists.newArrayList(deviceCode));
        //按照deviceCode + . + dit + . + remoteType转map
        Map<String,Double> ykYtRecordMap = Maps.newHashMap();
        if(!CollectionUtils.isEmpty(ykYtRecordList)){
            ykYtRecordList.stream().forEach(f->{
                ykYtRecordMap.put(f.getDeviceCode() + SysConstant.SPOT + f.getDit() + SysConstant.SPOT + f.getRemoteType(), f.getValue());
            });
        }
        deviceYkYtVOList.stream().forEach(d->{
            if(d.getActualPid() != null && actualAndFeedBackMap.get(d.getActualPid()) != null){
                d.setActualValVO(this.getFlashDataByYcYxPid(emsDevice,actualAndFeedBackMap.get(d.getActualPid())));
            }
            if(d.getFeedBackPid() != null && actualAndFeedBackMap.get(d.getFeedBackPid()) != null){
                d.setFeedbackValVO(this.getFlashDataByYcYxPid(emsDevice,actualAndFeedBackMap.get(d.getFeedBackPid())));
            }
            d.setLatestIssuedVal(ykYtRecordMap.get(d.getDeviceCode() + SysConstant.SPOT + d.getDit() + SysConstant.SPOT + d.getRemoteType()));
        });
        return DeviceRemoteControlVO.builder()
                .deviceCode(deviceCode).deviceName(emsDevice.getDeviceName()).deviceType(emsDevice.getDeviceType())
                .ykYtList(deviceYkYtVOList.stream().sorted(Comparator.comparing(DeviceYkYtVO::getSortNumber)).collect(Collectors.toList()))
                .build();
    }

    /**
     * 获取设备遥控数据
     * @param deviceCode
     * @return
     */
    public DeviceRemoteControlVO getDeviceYkData(String deviceCode){
        EmsDevice emsDevice = emsDeviceDAO.selectByDeviceCode(deviceCode);
        Assert.notNull(emsDevice,"设备不存在!");
        //虚拟设备不存在遥控数据
        if(emsDevice.getProductKey() == null){
            return DeviceRemoteControlVO.builder().deviceCode(deviceCode).deviceName(emsDevice.getDeviceName()).ykYtList(Collections.emptyList()).build();
        }
        //获取设备对应的遥控点号有哪些
        List<IotViceProductYkYtExt> ykExtList = extIotViceProductYkDAO.getBaseMapper().selectYkByProductKeyAndModuleCode(emsDevice.getInitDeviceCode(), emsDevice.getModuleCode());
        //这里是获取遥调点号作为遥控使用的点号有哪些
        ykExtList.addAll(extIotViceProductYtDAO.getBaseMapper().selectYkByDeviceCodeAndModuleCode(
                emsDevice.getInitDeviceCode(),emsDevice.getModuleCode()));
        if(CollectionUtils.isEmpty(ykExtList)){
            return DeviceRemoteControlVO.builder().deviceCode(deviceCode).deviceName(emsDevice.getDeviceName()).ykYtList(Collections.emptyList()).build();
        }
        List<DeviceYkYtVO> deviceYkYtVOList = Lists.newArrayList();
        ykExtList.stream().forEach(d->{
            DeviceYkYtVO deviceYkYtVO = DeviceYkYtVO.builder()
                    .ditName(d.getRemark()).remoteType(d.getRemoteType()).ditType(Objects.equals(d.getRemoteType(), RemoteType.YK.getCode())?DitTypeEnum.YK.getCode():DitTypeEnum.YT.getCode())
                    .dit(d.getDit()).deviceCode(deviceCode).initDeviceCode(emsDevice.getInitDeviceCode())
                    .pid(emsDevice.getInitDeviceCode() + SysConstant.SPOT + d.getDit()).unit(d.getUnit())
                    .rangeStart(d.getRangeStart()).rangeEnd(d.getRangeEnd()).sortNumber(d.getOrderNum())
                    .build();
            //分装下发值
            if(d.getValSplit() != null){
                List<IssueValSplitVO> splitVOList = JSON.parseArray(d.getValSplit(),IssueValSplitVO.class)
                        .stream().sorted(Comparator.comparing(IssueValSplitVO::getOrderNum)).collect(Collectors.toList());;
                deviceYkYtVO.setIssueValList(splitVOList);
            }else {
                //没有配置下发数据,采用默认的下发配置,1:合,0:分
                List<IssueValSplitVO> splitVOlist = Lists.newArrayList();
                splitVOlist.add(IssueValSplitVO.builder().issueVal(1).name("合").orderNum(1).build());
                splitVOlist.add(IssueValSplitVO.builder().issueVal(0).name("分").orderNum(2).build());
            }
            deviceYkYtVOList.add(deviceYkYtVO);
        });
        //查询遥调历史数据信息
        List<EmsYkYtRecord> ykYtRecordList = emsYkYtRecordDAO.getListByDeviceCodes(Lists.newArrayList(deviceCode));
        //按照deviceCode + . + dit + . + remoteType转map
        Map<String,Double> ykYtRecordMap = Maps.newHashMap();
        if(!CollectionUtils.isEmpty(ykYtRecordList)){
            ykYtRecordList.stream().forEach(f->{
                ykYtRecordMap.put(f.getDeviceCode() + SysConstant.SPOT + f.getDit() + SysConstant.SPOT + f.getRemoteType(), f.getValue());
            });
        }
        deviceYkYtVOList.stream().forEach(d->{
            d.setLatestIssuedVal(ykYtRecordMap.get(d.getDeviceCode() + SysConstant.SPOT + d.getDit() + SysConstant.SPOT + d.getRemoteType()));
        });
        return DeviceRemoteControlVO.builder()
                .deviceCode(deviceCode).deviceName(emsDevice.getDeviceName()).deviceType(emsDevice.getDeviceType())
                .ykYtList(deviceYkYtVOList.stream().sorted(Comparator.comparing(DeviceYkYtVO::getSortNumber)).collect(Collectors.toList()))
                .build();
    }

    private FlashVO getFlashDataByYcYxPid(EmsDevice emsDevice,FlashDataVO flashDataVO){
        FlashVO flashVO = FlashVO.builder()
                .pid(flashDataVO.getPid()).deviceCode(emsDevice.getDeviceCode()).initDeviceCode(emsDevice.getInitDeviceCode())
                .dit(flashDataVO.getDit())
                .ditType(MeasureType.yx.getCodeInIot().equals(flashDataVO.getMeasureType())? DitTypeEnum.YX.getCode() : DitTypeEnum.YC.getCode())
                .name(flashDataVO.getRemark()).unit(flashDataVO.getUnit())
                .value(flashDataVO.getVal())
                .time(flashDataVO.getTime())
                .build();
        return flashVO;
    }

    private FlashVO getFlashDataByExtIotDeviceCalculation(EmsDevice emsDevice,ExtIotDeviceCalculation calculation,FlashDataVO flashDataVO){
        if(flashDataVO != null){
            FlashVO flashVO = JacksonUtil.convertToObj(flashDataVO, FlashVO.class);
            if(isJson(flashDataVO.getVal())){
                PidInfoVO pidInfoVO = JSON.parseObject(flashDataVO.getVal(), PidInfoVO.class);
                flashVO.setValue(pidInfoVO.getVal());
                flashVO.setExtraInfo(BMSKindUtil.getClusterByCondition(pidInfoVO));
            }else {
                flashVO.setValue(flashDataVO.getVal());
            }
            flashVO.setName(calculation.getComputeName());
            return flashVO;
        }
        FlashVO flashVO = FlashVO.builder()
                .pid(calculation.getComputeCode()).deviceCode(emsDevice.getDeviceCode()).initDeviceCode(emsDevice.getInitDeviceCode())
                .dit(calculation.getComputeCode().replace(emsDevice.getInitDeviceCode() + SysConstant.SPOT,""))
                .ditType(MeasureType.yx.getCodeInIot().equals(calculation.getMeasureType())? DitTypeEnum.CAL_YX.getCode() : DitTypeEnum.CAL_YC.getCode())
                .name(calculation.getComputeName()).unit(calculation.getUnit())
                .build();
        return flashVO;
    }

    private static boolean isJson(String str) {
        boolean result = false;
        if (StringUtils.isNotBlank(str)) {
            str = str.trim();
            if (str.startsWith("{") && str.endsWith("}")) {
                result = true;
            } else if (str.startsWith("[") && str.endsWith("]")) {
                result = true;
            }
        }
        return result;
    }

}
